/**
 * 
 */
package com.zoiz.plus.extend;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;

import com.zoiz.plus.ActionContext;


/**
 * 解析请求参数至java对象
 * 
 * @author shmiluyu
 * 
 */
public class ParamsParser {

	/**
	 * 根据提供的前缀解析出javabean<br>
	 * 例如：<br>
	 * <code>
	 * &ltinput name="bean.title"&gt<br>
	 * ParamsParser.toObject("bean.",ExampleBean.class);<br>
	 * <code>
	 * 会自动生成一个ExampleBean实例并会调用setTitle方法为title赋值
	 * 
	 * @param prefix
	 * @param clazz
	 * @return
	 */
	public static final <T> T toObject(String prefix, Class<T> clazz) {
		try {
			T result = newInstance(clazz);
			PropertyDescriptor[] props = propertyDescriptors(clazz);

			Map<String, String[]> paramMap = ActionContext.getActionContext()
					.getHttpServletRequest().getParameterMap();
			for (String key : paramMap.keySet()) {
				if (key.startsWith(prefix)) {
					String propertyName = key.split("\\.")[1];
					if (StringUtils.isNotBlank(paramMap.get(key)[0].toString()))
						BeanUtils.setProperty(result, propertyName,
								paramMap.get(key));

				}
			}
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			throw new ParamsParserException("Can not parse to Object:["
					+ clazz.getName() + "]", e.getCause());
		}
	}

	/**
	 * 解析到一个List<br>
	 * 参考{@link #toObject(String, Class)}
	 * 
	 * @param prefix
	 * @param clazz
	 * @return
	 */
	public static <T> List<T> toList(String prefix, Class<T> clazz) {
		try {
			Map<String, String> paramMap = ActionContext.getActionContext()
					.getHttpServletRequest().getParameterMap();
			Set<String> keySet = new HashSet<String>();
			for (String key : paramMap.keySet()) {
				if (key.startsWith(prefix))
					keySet.add(key.split("\\.")[0]);
			}
			List<T> result = new ArrayList<T>();
			for (String keyPrefix : keySet) {
				result.add(toObject(keyPrefix, clazz));
			}
			return result;
		} catch (Exception e) {
			throw new ParamsParserException("Can not parse to list:["
					+ clazz.getName() + "]", e.getCause());
		}
	}

	static final Object covertStringTo(String strValue, Class<?> propType) {
		if (propType.equals(String.class)) {
			return strValue;
		} else if (propType.equals(Integer.TYPE)
				|| propType.equals(Integer.class)) {
			return Integer.parseInt(strValue);
		} else if (propType.equals(Boolean.TYPE)
				|| propType.equals(Boolean.class)) {
			return new Boolean(strValue);
		} else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {
			return Long.parseLong(strValue);
		} else if (propType.equals(Date.class)) {
			return new Date(strValue);
		} else {
			return strValue;
		}
	}

	static final <T> T newInstance(Class<T> c) throws ParamsParserException {
		try {
			return c.newInstance();
		} catch (InstantiationException e) {
			throw new ParamsParserException("Cannot create instance "
					+ c.getName(), e.getCause());

		} catch (IllegalAccessException e) {
			throw new ParamsParserException("Cannot create instance "
					+ c.getName(), e.getCause());
		}

	}

	static PropertyDescriptor[] propertyDescriptors(Class<?> c) {
		// Introspector caches BeanInfo classes for better performance
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(c);
		} catch (java.beans.IntrospectionException e) {
			throw new ParamsParserException("Bean introspection failed: "
					+ c.getName(), e.getCause());
		}
		return beanInfo.getPropertyDescriptors();
	}

	static boolean isCompatibleType(Object value, Class<?> type) {
		// Do object check first, then primitives
		if (value == null || type.isInstance(value)) {
			return true;
		} else if (type.equals(Integer.TYPE) && Integer.class.isInstance(value)) {
			return true;

		} else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
			return true;
		} else if (type.equals(Double.TYPE) && Double.class.isInstance(value)) {
			return true;
		} else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
			return true;
		} else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
			return true;
		} else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
			return true;
		} else if (type.equals(Character.TYPE)
				&& Character.class.isInstance(value)) {
			return true;
		} else if (type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
			return true;

		} else {
			return false;
		}

	}

}
